﻿using RFBR.Core.Infrastructure.Models.Structure;
using Xml = RFBR.Core.Infrastructure.XmlModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RFBR.DataAccess;

namespace RFBR.Core.Infrastructure
{
    public class XmlParser
    {
        private Xml.KnowledgeBase _schema;
        private IKnowledgeBase _model;
        public static XmlParser Instance
        {
            get
            {
                return new XmlParser();
            }
        }

        public IKnowledgeBase GetModel(Xml.KnowledgeBase schema)
        {
            _schema = schema;
            _model = new KnowledgeBase(schema.Name);
            InitFrames(schema.Frames);
            return _model;
        }

        public Xml.KnowledgeBase GetSchema(IKnowledgeBase kb)
        {
            Xml.KnowledgeBase kbase = new Xml.KnowledgeBase();
            kbase.Name = kb.Name;
            int framesCount = kb.Frames.Count();
            if (framesCount > 0)
            {
                kbase.Frames = new Xml.Frame[framesCount];
                List<Xml.CustomObject> customObjects = new List<Xml.CustomObject>();
                List<Xml.Variable> variables = new List<Xml.Variable>();
                for (int i = 0; i < framesCount; i++)
                {
                    IFrame frame = ((List<IFrame>)kb.Frames)[i];
                    Xml.Frame xmlframe = new Xml.Frame()
                    {
                        Id = frame.Id,
                        Name = frame.Name,
                        IsSubFrame = frame.IsSubFrame
                    };
                    if (frame.Parent != null)
                    {
                        xmlframe.Parent = frame.Parent.Id;
                    }
                    var slots = new List<Xml.Slot>();
                    foreach (ISlot slot in frame.Slots)
                    {
                        slots.Add(new Xml.Slot()
                        {
                            Name = slot.Name,
                            InheritanceType = slot.Inheritance,
                            Value = slot.Value.Id
                        });
                        switch (slot.Value.Type)
                        {
                            case EntityType.Custom:
                                customObjects.Add(new Xml.CustomObject()
                                {
                                    Id = slot.Value.Id,
                                    Name = slot.Value.Name,
                                    Value = ((ICustomObject)slot.Value).Value
                                });
                                break;
                            case EntityType.Variable:
                                variables.Add(new Xml.Variable()
                                {
                                    Id = slot.Value.Id,
                                    Name = slot.Value.Name,
                                    Domain = new Xml.Domain()
                                    {
                                        Items = ((IVariable)slot.Value).Domain.Items.ToArray()
                                    },
                                    Value = ((IVariable)slot.Value).Value
                                });
                                break;
                        }
                    }
                    xmlframe.Slots = slots.ToArray();
                    kbase.Frames[i] = xmlframe;
                }
                kbase.CustomObjects = customObjects.ToArray();
                kbase.Variables = variables.ToArray();
            }
            return kbase;
        }

        private void InitFrames(Xml.Frame[] frameNodes)
        {
            foreach (var f in frameNodes)
            {
                SetFrame(f);
            }
            foreach (var f in frameNodes)
            {
                if (f.Parent.HasValue)
                {
                    IFrame frame = _model.GetFrameById(f.Id);
                    frame.Parent = _model.GetFrameById(f.Parent.Value);
                    InitSlots(frame, f.Slots);
                }
            }
        }

        private IFrame SetFrame(Xml.Frame frame)
        {
            IFrame f = _model.SetFrame(frame.Id, frame.Name, frame.Parent.HasValue ? _model.GetFrameById(frame.Parent.Value) : null, frame.IsSubFrame);
            return f;
            //InitSlots(f, frame.Slots);
        }

        private void InitSlots(IFrame frame, Xml.Slot[] slots)
        {
            foreach (var s in slots)
            {
                SetSlot(frame, s);
            }
        }

        private void SetSlot(IFrame frame, Xml.Slot slot)
        {
            ISlot s = frame.CreateSlot(slot.Name, FindEntityTypeById(slot.Value), slot.InheritanceType);
            s.Value = FindEntityById(slot.Value);
        }

        private IEntity FindEntityById(Guid guid)
        {
            foreach (var co in _schema.CustomObjects)
            {
                if (co.Id == guid)
                {
                    return _model.CreateCustomObject(co.Id, co.Name, co.Value);
                }
            }

            foreach (var v in _schema.Variables)
            {
                if (v.Id == guid)
                {
                    var variable = _model.CreateVariable();
                    SetVariable(variable, v);
                    return variable;
                }
            }

            foreach (var f in _schema.Frames)
            {
                if (f.Id == guid)
                {
                    return _model.GetFrameById(guid);
                }
            }

            return null;
        }

        private EntityType FindEntityTypeById(Guid guid)
        {
            foreach (var co in _schema.CustomObjects)
            {
                if (co.Id == guid)
                {
                    return EntityType.Custom;
                }
            }

            foreach (var v in _schema.Variables)
            {
                if (v.Id == guid)
                {
                    return EntityType.Variable;
                }
            }

            foreach (var f in _schema.Frames)
            {
                if (f.Id == guid)
                {
                    return EntityType.Frame;
                }
            }

            return EntityType.Custom;
        }

        private void SetVariable(IVariable var, Xml.Variable xmlVar)
        {
            var.CreateNewDomain().Items = xmlVar.Domain.Items.ToList();
            var.Value = xmlVar.Value;
        }

        public IFrame UpdateFrame(IKnowledgeBase model, dynamic frame)
        {
            _model = model;
            return SetFrame(frame);
        }
    }
}
